#include "CNativeHandlerWindows.h"
#include "CNativeHandlerPrivate.h"

#ifdef Q_OS_WINDOWS

#include <QEvent>
#include <QScreen>
#include <QWindow>

#include <dwmapi.h>
#include <windows.h>
#include <windowsx.h>

CNativeHandlerWindows::CNativeHandlerWindows(QWidget *parent) : CNativeHandler(parent) {
    hideWindowFrame();
    m_hasInitWindow = false;
    m_hasSetResizable = false;
}

CNativeHandlerWindows::~CNativeHandlerWindows() {
}

void CNativeHandlerWindows::showForward() {
    HWND winId = HWND(w->winId());
    ::SetWindowPos(winId, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
    ::SetWindowPos(winId, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
    w->show();
    w->activateWindow();
}

void CNativeHandlerWindows::updateResizable() {
    m_hasSetResizable = false;

    if (!w->testAttribute(Qt::WA_WState_Created) || !w->internalWinId()) {
        return;
    }
    // Set Maximize Box
    HWND hwnd = HWND(w->winId());
    DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
    if (m_resizable) {
        ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX);
    } else {
        ::SetWindowLong(hwnd, GWL_STYLE, style & ~WS_MAXIMIZEBOX);
    }

    m_hasSetResizable = true;
}

void CNativeHandlerWindows::winIdChangeEventEx(QEvent *event) {
    Q_UNUSED(event)
    if (!m_hasInitWindow) {
        HWND hwnd = HWND(w->winId());
        DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
        ::SetWindowLong(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION);
        const MARGINS shadow = {1, 1, 1, 1};
        DwmExtendFrameIntoClientArea(hwnd, &shadow);
        m_hasInitWindow = true;
    }
    if (!m_hasSetResizable) {
        updateResizable();
    }
}

bool CNativeHandlerWindows::commonEventFilter(QEvent *event) {
    return CNativeHandler::commonEventFilter(event);
}

bool CNativeHandlerWindows::nativeEventFilter(const QByteArray &eventType, void *message,
                                              long *result) {
    if (eventType != "windows_generic_MSG") {
        return false;
    }

    MSG *msg = static_cast<MSG *>(message);
    QWidget *widget = QWidget::find(reinterpret_cast<WId>(msg->hwnd));
    if (!widget) {
        return false;
    }

    QScreen *screen = w->windowHandle()->screen();
    double m_dpiScale = screen->devicePixelRatio();

    switch (msg->message) {
    case WM_NCCALCSIZE: {
        *result = 0;
        return true;
    }

    case WM_NCHITTEST: {
        RECT winrect;
        GetWindowRect(HWND(w->winId()), &winrect);

        int x = GET_X_LPARAM(msg->lParam);
        int y = GET_Y_LPARAM(msg->lParam);

        if (w->windowState() == Qt::WindowFullScreen) {
            *result = HTCLIENT;
            return true;
        }

        const QMargins &bd = m_resizeMargins;
        if (m_resizable) {
            bool resizeWidth = w->minimumWidth() != w->maximumWidth();
            bool resizeHeight = w->minimumHeight() != w->maximumHeight();

            if (resizeWidth) {
                // left border
                if (x >= winrect.left && x < winrect.left + bd.left()) {
                    *result = HTLEFT;
                }
                // right border
                if (x < winrect.right && x >= winrect.right - bd.right()) {
                    *result = HTRIGHT;
                }
            }
            if (resizeHeight) {
                // bottom border
                if (y < winrect.bottom && y >= winrect.bottom - bd.bottom()) {
                    *result = HTBOTTOM;
                }
                // top border
                if (y >= winrect.top && y < winrect.top + bd.top()) {
                    *result = HTTOP;
                }
            }
            if (resizeWidth && resizeHeight) {
                // bottom left corner
                if (x >= winrect.left && x < winrect.left + bd.left() && y < winrect.bottom &&
                    y >= winrect.bottom - bd.bottom()) {
                    *result = HTBOTTOMLEFT;
                }
                // bottom right corner
                if (x < winrect.right && x >= winrect.right - bd.right() && y < winrect.bottom &&
                    y >= winrect.bottom - bd.bottom()) {
                    *result = HTBOTTOMRIGHT;
                }
                // top left corner
                if (x >= winrect.left && x < winrect.left + bd.left() && y >= winrect.top &&
                    y < winrect.top + bd.top()) {
                    *result = HTTOPLEFT;
                }
                // top right corner
                if (x < winrect.right && x >= winrect.right - bd.right() && y >= winrect.top &&
                    y < winrect.top + bd.top()) {
                    *result = HTTOPRIGHT;
                }
            }
        }
        if (*result != HTNOWHERE) {
            return true;
        }
        *result = HTCLIENT;

        // Check if c on caption
        Q_D_MOUSE_AVAILABLE
        if (d) {
            *result = HTCAPTION;
            return true;
        }
        return true;
    }

    case WM_GETMINMAXINFO: {
        if (::IsZoomed(msg->hwnd)) {
            RECT frame = {0, 0, 0, 0};
            AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
            frame.left = abs(frame.left);
            frame.top = abs(frame.bottom);
            widget->setContentsMargins(int(frame.left / m_dpiScale), int(frame.top / m_dpiScale),
                                       int(frame.right / m_dpiScale),
                                       int(frame.bottom / m_dpiScale));
        } else {
            widget->setContentsMargins(0, 0, 0, 0);
        }
        *result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
        return true;
    }

    default:
        break;
    }
    return CNativeHandler::nativeEventFilter(eventType, message, result);
}

#endif
